const image = document.getElementById("target");

/**
 * 使用 Fetch API 获取文件默认是使用 Promise 模式返回 Stream 数据
 */
fetch('/images/tortoise.png')
.then(response => response.body)
.then(res => {
  // res 是一个可读流对象 ReadableStream
  console.log(res);

  /**
   * ReadableStream 实例下的 getReader 方法，可以给入创建 mode (byob/不给入默认) 创建相应功能的流读取器
   * 并同时锁定当前流，不能被其他读取器获取，知道这个流释放
   */
  const reader = res.getReader();

  return new ReadableStream({
    /**
     * 读取流实例创建时调用；
     * 其内容旨在访问流资源和设置流功能
     */
    async start(controller) {
      console.log(controller);
      while(true) {
        /**
         * ReadableStreamDefaultReader.read 方法返回一个 Promise，用于访问流内部队列的下一个块
         */
        const { done, value } = await reader.read();

        // 判断流是否结束，如果流中没有数据，则跳出循环
        if (done) break;

        // 将下一个块数据放入目标流的队列中
        controller.enqueue(value);
      }

      // 当流读取完成，跳出时关闭当前读取流
      controller.close();
      // 释放源读取流
      reader.releaseLock();
    },
    
    /**
     * 如果当前流的内部队列没有满，此方法会被重复调用，直到达到最该水位线
     * 如果返回一个 Promise 则在这个 Promise fulfills 前不会重复调用
     * 如果这个 Promise 被 rejectd 整个流会报错
     */
    pull() {
      console.log('pull')
    },

    /**
     * 当流被 cancel 时调用，内容包括任何在流关闭时要做的工作，当异步时可以返回 Promise 来指定成功或失败
     */
    cancel() {
      console.log('cancel');
    }
  })
})
// 创建新的包含有流的返回对象
.then(rs => new Response(rs))
// 创建一个 Object URL
.then(response => response.blob())
.then(blob => URL.createObjectURL(blob))
// 设置图片
.then(url => image.src = url)
.catch(console.error);